﻿using Sys.Core;
using Sys.Core.Models.Dto;
using Sys.Core.Models.Queries;
using Sys.Core.Models.VO;
using MiniFox;
using MiniFox.Components;
using MiniFox.Data.EntityFramework;
using MiniFox.Infrastructure;
using MiniFox.Platform;

using System.Linq;
using System.Security.Cryptography;

namespace Sys.Data
{
    [AutoResolve]
    public class OrgProvider : EntityDataProvider<PlatformDbContext>, IOrgProvider
    {
        private OrganizationVo MappingToVo(SysOrganization sysOrg)
        {
            if (sysOrg == null) return null;
            return new OrganizationVo
            {
                OrganizationID = sysOrg.OrgId,
                OrganizationName = sysOrg.OrgName,
                Description = sysOrg.Description,
                DisplayName = sysOrg.DisplayName,
                Logo = sysOrg.Logo,
                Icon = sysOrg.Icon,
                CreateTime = sysOrg.CreateTime,
                UpdateTime = sysOrg.UpdateTime
            };
        }
        public ResultObject<IEnumerable<OrganizationVo>> Query(PagedQueryObject<OrgQuery> request)
        {
            var result = this.PagedQueryExecute<SysOrganization>(request);
            return result.GetResultObject(sysOrg => new OrganizationVo
            {
                OrganizationID = sysOrg.OrgId,
                OrganizationName = sysOrg.OrgName,
                Description = sysOrg.Description,
                DisplayName = sysOrg.DisplayName,
                Logo = sysOrg.Logo,
                Icon = sysOrg.Icon,
                CreateTime = sysOrg.CreateTime,
                UpdateTime = sysOrg.UpdateTime
            });
        }

        public OrganizationVo GetOrganization(string orgId)
        {
            var sysOrg = this.CurrentDbContext.SysOrganization.SingleOrDefault(o=>o.OrgId == orgId);
            return MappingToVo(sysOrg);
        }

        public bool CreateOrganization(OrganizationDto organization)
        {
            var sysOrg = new SysOrganization();
            sysOrg.OrgId = organization.OrganizationID;
            sysOrg.OrgName = organization.OrganizationName;
            sysOrg.DisplayName = organization.DisplayName;
            sysOrg.Description = organization.Description;
            sysOrg.Logo = organization.Logo;
            sysOrg.Icon = organization.Icon;
            sysOrg.CreateTime = DateTime.Now;
            sysOrg.UpdateTime = DateTime.Now;

            var sysDept = new SysDept();
            sysDept.DeptId = SequenceGUID.NewGUID();
            sysDept.OrgId = sysOrg.OrgId;
            sysDept.ParentId = sysOrg.OrgId;
            sysDept.DeptName = organization.OrganizationName;
            sysDept.DeptPath = $"/{sysOrg.OrgName}";
            sysDept.Ordinal = 0;
            sysDept.Enabled = true;
            return this.Insert(sysOrg) > 0;
        }

        public bool UpdateOrganization(OrganizationDto organization)
        {
            var sysOrg = this.CurrentDbContext.SysOrganization.SingleOrDefault(o => o.OrgId == organization.OrganizationID);
            if (sysOrg == null) return false;
            sysOrg.OrgName = organization.OrganizationName;
            sysOrg.DisplayName = organization.DisplayName;
            sysOrg.Description = organization.Description;
            sysOrg.Logo = organization.Logo;
            sysOrg.Icon = organization.Icon;
            sysOrg.UpdateTime = DateTime.Now;
            return this.Update(sysOrg) > 0;
        }

        public bool DeleteOrganization(params string[] organizations)
        {
            List<object> list = new List<object>();
            foreach(var orgId in organizations)
            {
                //var depts = this.CurrentDbContext.SysDept.Where(d => d.OrgId == orgId).ToArray();
                //list.AddRange(depts);
                var sysOrg = this.CurrentDbContext.SysOrganization.SingleOrDefault(o => o.OrgId == orgId);
                list.Add(sysOrg);
            }
            return this.Delete(list.ToArray()) > 0;
        }

        #region dept
        public IEnumerable<DeptVo> GetDepts(string orgId)
        {
            var depts = (from d in this.CurrentDbContext.SysDept
                         where d.OrgId == orgId
                         select new DeptVo
                         {
                             OrganizationID = orgId,
                             DeptID = d.DeptId,
                             DeptName = d.DeptName,
                             DeptPath = d.DeptPath,
                             Description = d.Description,
                             Director = d.Director,
                             ParentDeptID = d.ParentId
                         });
            return depts.ToArray();
        }

        public bool DeptExists(string deptPath)
        {
            return this.CurrentDbContext.SysDept.Any(d => d.DeptPath == deptPath);
        }

        public bool CreateDept(DeptDto dept)
        {
            var sysDept = new SysDept();
            sysDept.DeptId = dept.DeptID;
            sysDept.DeptPath = dept.DeptPath;
            sysDept.ParentId = dept.ParentDeptID;
            sysDept.OrgId = dept.OrganizationID;
            sysDept.DeptName = dept.DeptName;
            sysDept.Description = dept.Description;
            sysDept.Director = dept.Director;
            sysDept.Ordinal = dept.Ordinal;
            sysDept.Enabled = true;
            return this.Insert(sysDept) > 0;
        }

        public bool UpdateDept(DeptDto dept)
        {
            var sysDept = this.CurrentDbContext.SysDept.SingleOrDefault(d => d.DeptId == dept.DeptID && d.OrgId == dept.OrganizationID);
            var prefix = $"{sysDept.DeptPath}/";
            sysDept.DeptPath = dept.DeptPath;
            sysDept.DeptName = dept.DeptName;
            sysDept.Description = dept.Description;
            sysDept.Director = dept.Director;
            sysDept.Ordinal = dept.Ordinal;
            List<SysDept> sysDepts = new List<SysDept>
            {
                sysDept
            };

            var children = this.CurrentDbContext.SysDept.Where(d => d.DeptPath.StartsWith(prefix)).ToArray();

            foreach (var childDept in children)
            {
                childDept.DeptPath = childDept.DeptPath.Replace(prefix, $"{sysDept.DeptPath}/");
                sysDepts.Add(childDept);
            }

            return this.Update(sysDepts.ToArray()) > 0;
        }
        public bool DeleteDept(string orgId, string deptId)
        {
            var sysDept = this.CurrentDbContext.SysDept.SingleOrDefault(d => d.OrgId == orgId && d.DeptId == deptId);
            return this.DeleteDept(sysDept);
        }
        public bool DeleteDept(string deptPath)
        {
            var sysDept = this.CurrentDbContext.SysDept.SingleOrDefault(d => d.DeptPath == deptPath);
            return this.DeleteDept(sysDept);
        }

        private bool DeleteDept(SysDept sysDept)
        {
            List<object> list = new List<object>();
            //var deptUsers = this.CurrentDbContext.SysUserDept.Where(u => u.DeptPath == sysDept.DeptPath || u.DeptPath.StartsWith($"{sysDept.DeptPath}/")).ToArray();
            //list.AddRange(deptUsers);
            var subDepts = this.CurrentDbContext.SysDept.Where(m => m.DeptPath.StartsWith($"{sysDept.DeptPath}/")).ToArray();
            list.AddRange(subDepts);
            list.Add(sysDept);

            return this.Delete(list.ToArray()) > 0;
        }

        public DeptVo GetDept(string orgId, string deptId)
        {
            var d = this.CurrentDbContext.SysDept.SingleOrDefault(d => d.DeptId == deptId && d.OrgId == orgId);
            return new DeptVo
            {
                OrganizationID = d.OrgId,
                DeptID = d.DeptId,
                DeptName = d.DeptName,
                DeptPath = d.DeptPath,
                Description = d.Description,
                Director = d.Director,
                Ordinal = d.Ordinal,
                ParentDeptID = d.ParentId
            };
        }

        public DeptVo GetDept(string deptPath)
        {
            var d = this.CurrentDbContext.SysDept.SingleOrDefault(d => d.DeptPath == deptPath);
            return new DeptVo
            {
                OrganizationID = d.OrgId,
                DeptID = d.DeptId,
                DeptName = d.DeptName,
                DeptPath = d.DeptPath,
                Description = d.Description,
                Director = d.Director,
                ParentDeptID = d.ParentId, 
                Enabled = d.Enabled
            };
        }

        public ResultObject<IEnumerable<UserVo>> GetUsers(string orgId, string deptId, PagedQueryObject<UserQuery> query)
        {
            var queryable = (from u in this.CurrentDbContext.SysUser
                             join du in this.CurrentDbContext.SysUserDept on u.UserId equals du.UserId
                             join d in this.CurrentDbContext.SysDept on du.DeptPath equals d.DeptPath
                             where d.OrgId == orgId && d.DeptId == deptId
                             select u);
            var result = this.PagedQueryExecute(queryable, query);
            return result.GetResultObject<UserVo>(u => new UserVo
            {
                UserID = u.UserId,
                UpdateTime = u.UpdateTime,
                Cellphone = u.Cellphone,
                CreateTime = u.CreateTime,
                DisplayName = u.DisplayName,
                LoginName = u.LoginName,
                SecEmail = u.SecEmail,
                UserStatus = Enum.Parse<IdentityStatus>(u.UserStatus),
            });
        }
        public ResultObject<IEnumerable<UserVo>> ExclusionUsers(string orgId, string deptId, PagedQueryObject<UserQuery> query)
        {
            var deptUsers = (from du in this.CurrentDbContext.SysUserDept
                             join d in this.CurrentDbContext.SysDept on du.DeptPath equals d.DeptPath
                             where d.OrgId == orgId && d.DeptId == deptId
                             select du.UserId);
            var queryable = (from u in this.CurrentDbContext.SysUser
                             where !deptUsers.Contains(u.UserId)
                             select u);
            var result = this.PagedQueryExecute(queryable, query);
            return result.GetResultObject<UserVo>(u => new UserVo
            {
                UserID = u.UserId,
                UpdateTime = u.UpdateTime,
                Cellphone = u.Cellphone,
                CreateTime = u.CreateTime,
                DisplayName = u.DisplayName,
                LoginName = u.LoginName,
                SecEmail = u.SecEmail,
                UserStatus = Enum.Parse<IdentityStatus>(u.UserStatus),
            });
        }
        public bool AddDeptUsers(string deptPath, string[] users)
        {
            List<SysUserDept> deptUsers = new List<SysUserDept>();
            foreach (var userId in users)
            {
                deptUsers.Add(new SysUserDept
                {
                    UserId = userId,
                    DeptPath = deptPath
                });
            }
            return this.Insert(deptUsers.ToArray()) > 0;
        }

        public bool RemoveDeptUsers(string deptPath, string[] users)
        {
            var deptUsers = (from du in this.CurrentDbContext.SysUserDept
                             where users.Contains(du.UserId) && du.DeptPath == deptPath
                             select du).ToArray();
            return this.Delete(deptUsers) > 0;
        }

        public bool AddDeptUsers(string orgId, string deptId, string[] users)
        {
            var dept = this.CurrentDbContext.SysDept.SingleOrDefault(d => d.OrgId == orgId && d.DeptId == deptId);
            return AddDeptUsers(dept.DeptPath, users);
        }

        public bool removeDeptUsers(string orgId, string deptId, string[] users)
        {
            var deptUsers = (from du in this.CurrentDbContext.SysUserDept 
                             join d in this.CurrentDbContext.SysDept on du.DeptPath equals d.DeptPath
                             where d.OrgId == orgId && d.DeptId == deptId
                             select du).ToArray();
            return this.Delete(deptUsers) > 0;
        }

        #endregion
    }
}
